package com.hhdz.barrage.work;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;

import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.widget.Toast;

import com.hhdz.barrage.utils.JSONTokener;
import com.hhdz.barrage.utils.Logcat;

public final class Worker {

	private final Context mContext;
	private final ConnectivityManager mConnectivityManager;
	private final HandlerThread mSocketThread = new HandlerThread("SOCKET");
	private final HandlerThread mSocketReadThread = new HandlerThread("SOCKET:READ");
	private final Handler mSocketHandler;
	private final Handler mSocketReadHandler;
	private final Handler mUiHandler;

	private Socket mSocket = null;
	private boolean mConnected = false;
	private boolean isListening = true;

	private ServerSocket server = null;

	public static interface ConnectionStateChangedCallback {

		public static final int STATE_CONNECTED = 1;
		public static final int STATE_DISCONNECTED = 2;

		public void onConnectionStateChanged(int state);

	}

	private ConnectionStateChangedCallback mConnectionStateChangedCallback = null;

	public static interface DataReceivedCallback {

		public void onDataReceived(JSONObject json);

	}

	private DataReceivedCallback mDataReceivedCallback = null;

	public Worker(Context context) {
		Logcat.TRACE_CALL(context);
		mContext = context;
		mConnectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
		mSocketThread.start();
		mSocketHandler = new Handler(mSocketThread.getLooper());
		mSocketReadThread.start();
		mSocketReadHandler = new Handler(mSocketReadThread.getLooper());
		mUiHandler = new Handler(Looper.getMainLooper());
	}

	public void setConnectionListener(ConnectionStateChangedCallback cb) {
		mConnectionStateChangedCallback = cb;
	}

	public void setDataListener(DataReceivedCallback cb) {
		mDataReceivedCallback = cb;
	}

	public void terminate() {
		Logcat.TRACE_CALL();
		mSocketThread.quit();
		mSocketReadThread.quit();
	}

	public void start() {
		mSocketHandler.post(new Runnable() {
			@Override
			public void run() {
				listen();
			}
		});
	}

	public void listen() {
		try {
			Logcat.INFO("Wait connect...");
			if (server != null) {
				Logcat.INFO("Server exist");
				server.close();
			}
			server = new ServerSocket(9999);
			while (isListening) {
				final Socket socket = server.accept();
				Logcat.INFO("Get a connect...");
				mSocketReadHandler.post(new Runnable() {

					@Override
					public void run() {
						startReading(socket);
					}

				});
			}
		} catch (Exception e) {
			Logcat.INFO("Listener exception:"+e.toString());
			e.printStackTrace();
		}
	}

	private void toast(final String msg) {
		mUiHandler.post(new Runnable() {

			@Override
			public void run() {
				Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
			}

		});
	}

	public void startReading(final Socket socket) {
		Logcat.TRACE_CALL();
		try {
			if (socket == null) {
				toast("ERROR: mSocket == null");
				return;
			}
			final InputStream stm = socket.getInputStream();
			final byte[] buffer = new byte[4096];
			while (true) {
				final int size = stm.read(buffer);
				if (size == -1) return;
				String string = new String(buffer,0,size);
				Logcat.DEBUG("read: " + string);
				JSONTokener token = new JSONTokener(string);
				ArrayList<Object> values = token.values();
				for (Object object : values) {
					if (!(object instanceof JSONObject)) {
						continue;
					}
					final JSONObject json = (JSONObject) object;
//						Logcat.DEBUG("code: " + code);
					mUiHandler.post(new Runnable() {

						@Override
						public void run() {
							if (handleHeartbeatMsg(json)) { // handle heart beat msg
							} else { // hand msg
								mDataReceivedCallback.onDataReceived(json);
							}
						}

					});
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
			toast("ERROR: " + e.getMessage());
		} finally {
			mSocketHandler.post(new Runnable() {

				@Override
				public void run() {
					disconnect(socket);
				}

			});
		}
	}

	public void disconnect(Socket socket) {
		Logcat.TRACE_CALL();
		if (socket == null) {
			toast("ERROR: mSocket == null");
			return;
		}
		try {
			socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void write(String s) {
		Logcat.TRACE_CALL();
		try {
			if (mSocket == null) {
				toast("ERROR: mSocket == null");
				return;
			}
			mSocket.getOutputStream().write(s.getBytes());
		} catch (IOException e) {
			e.printStackTrace();
			toast("ERROR: " + e.getMessage());
		}
	}

	public void post(Runnable r) {
		mSocketHandler.post(r);
	}

	public void post(Runnable r,long delayTime) {
		mSocketHandler.postDelayed(r, delayTime);
	}

	public boolean isConnected() {
		return mConnected;
	}

    private boolean handleHeartbeatMsg(JSONObject json) {
        try {
            String reply = json.getString("order");
            if ("hb".equals(reply)) {
				return true;
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
		return false;
    }

}
